/* eslint-disable @typescript-eslint/no-explicit-any */
import { Component, type StyledProps } from '@knno/jsx';
import { inputBaseStyle } from './inputBase.css';
import '../popup/popup';

export type TextAlign = 'left' | 'center' | 'right';
export type IconType =
	| 'account'
	| 'organ'
	| 'address'
	| 'city'
	| 'key'
	| 'mail'
	| 'phone'
	| 'home'
	| 'edit';

export type InputBaseProps<T extends InputBase<any, any, any, V>, V> = StyledProps<T> & {
	name?: string;
	value?: V;
	title?: string;
	textAlign?: TextAlign;
	readOnly?: boolean;
	optional?: boolean;
	icon?: IconType;
	invalid?: string;
	validation?: (this: T) => string;
	onChange?: (this: T) => void;
};

export abstract class InputBase<
	P extends InputBaseProps<C, V>,
	C extends InputBase<P, C, E, V>,
	E extends HTMLElement,
	V,
> extends Component<P, C, E> {
	constructor(props: P) {
		super(props);
		void this.el;
		this.el.classList.add(inputBaseStyle);
		this.setReadOnly(this.readOnly);
		this.setTextAlign(this.textAlign);
		this.setTitle(this.title);
		this.setIcon(this.icon);
		this.setValue(this.value);
		this.setInvalid(this.invalid);
		const br = () => {
			if (this.el.childNodes.length === 0) {
				this.el.append(<br />);
			}
			this.el.dispatchEvent(new Event('contentchange'));
			this.setInvalid('');
		};
		const observer = new MutationObserver(br);
		observer.observe(this.el, { childList: true, characterData: true, subtree: false });
		br();
	}
	protected abstract renderContent(): void;
	protected abstract changeReadOnly(): void;

	get value(): V | undefined {
		return this.props.value;
	}

	protected updateValue(value: V | undefined, raiseEvent = true) {
		this.props.value = value;
		if (raiseEvent) {
			this.el.dispatchEvent(new Event('change'));
			this.props.onChange?.call(this as unknown as C);
		}
	}

	setValue(value: V | undefined): this {
		this.props.value = value;
		this.renderContent();
		return this;
	}

	get title(): string {
		return this.props.title ?? '';
	}

	setTitle(title: string): this {
		this.props.title = title;
		// this.el.title = title;
		return this;
	}

	get readOnly(): boolean {
		return this.props.readOnly ?? false;
	}
	setReadOnly(readOnly: boolean): this {
		this.props.readOnly = readOnly;
		this.el.classList.toggle('read-only', readOnly);
		this.changeReadOnly();
		return this;
	}

	get optional(): boolean {
		return this.props.optional ?? false;
	}
	setOptional(optional: boolean): this {
		this.props.optional = optional;
		return this;
	}

	get textAlign(): TextAlign {
		return this.props.textAlign ?? 'center';
	}
	setTextAlign(textAlign: TextAlign): this {
		this.props.textAlign = textAlign;
		this.el.style.textAlign = textAlign;
		return this;
	}
	get name(): string | undefined {
		return this.props.name;
	}
	setName(name: string | undefined): this {
		this.props.name = name;
		return this;
	}
	get icon(): IconType | undefined {
		return this.props.icon;
	}
	setIcon(icon: IconType | undefined): this {
		this.props.icon = icon;
		if (icon) {
			this.el.classList.add(`icon-${icon}`);
		} else {
			const removed = Array.from(this.el.classList).filter((item) => item.startsWith('icon-'));
			this.el.classList.remove(...removed);
		}
		return this;
	}
	get invalid(): string {
		return this.props.invalid ?? '';
	}
	setInvalid(invalid: string): this {
		this.props.invalid = invalid;
		if (invalid) {
			this.el.setAttribute('data-tips', invalid);
		} else {
			this.el.removeAttribute('data-tips');
		}
		return this;
	}

	setValidation(validation: (this: C) => string): this {
		this.props.validation = validation;
		return this;
	}

	validate(): boolean {
		if (this.props.validation) {
			const errMsg = this.props.validation.call(this as unknown as C);
			this.setInvalid(errMsg);
			return !errMsg;
		}
		return true;
	}

	onChange(onChange: ((this: C) => void) | undefined): this {
		this.props.onChange = onChange;
		return this;
	}
}
